package com.gulimall.cart.services.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.guigui.common.constant.CartConstant;
import com.guigui.common.utils.R;
import com.gulimall.cart.feign.ProductFeignService;
import com.gulimall.cart.interceptor.CookieInterceptor;
import com.gulimall.cart.services.CartServices;
import com.gulimall.cart.vo.Cart;
import com.gulimall.cart.vo.CartItem;
import com.gulimall.cart.vo.SkuInfo;
import com.gulimall.cart.vo.UserKeyInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;

@Service
public class CartServicesImpl implements CartServices {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    private ThreadPoolExecutor executor;

    @Override
    public CartItem addCart(Long skuId, Integer num) {
        BoundHashOperations<String, Object, Object> hashOperations = getCartOps();
        JSONObject item = (JSONObject) hashOperations.get(skuId.toString());
        if (item != null) {
            CartItem cartItemBySkuId = JSON.toJavaObject(item, CartItem.class);
            cartItemBySkuId.setCount(cartItemBySkuId.getCount() + num);
            hashOperations.put(skuId.toString(), cartItemBySkuId);
            return cartItemBySkuId;
        } else {
            CartItem cartItem = new CartItem();
            // 设置基本属性
            CompletableFuture<Void> setBaseAttrFuture = CompletableFuture.runAsync(() -> {
                R skuInfoBySkuId = productFeignService.getSkuInfoBySkuId(skuId);
                if (skuInfoBySkuId != null) {
                    SkuInfo skuInfo = skuInfoBySkuId.getData("skuInfo", new TypeReference<SkuInfo>() {
                    });
                    cartItem.setCheck(true);
                    cartItem.setImg(skuInfo.getSkuDefaultImg());
                    cartItem.setCount(num);
                    cartItem.setPrice(skuInfo.getPrice());
                    cartItem.setTitle(skuInfo.getSkuTitle());
                    cartItem.setTotalPrice(skuInfo.getPrice().multiply(new BigDecimal(num)));
                    cartItem.setSkuId(skuId);
                }
            }, executor);
            // 设置属性string list
            CompletableFuture<Void> setAttrStringListFuture = CompletableFuture.runAsync(() -> {
                List<String> skuStringList = productFeignService.getAttrStringListBySkuId(skuId);
                cartItem.setSkuAttr(skuStringList);
            }, executor);
            try {
                CompletableFuture.allOf(setBaseAttrFuture, setAttrStringListFuture).get();
            } catch (InterruptedException | ExecutionException ignored) {}
            hashOperations.put(skuId.toString(), cartItem);
            return cartItem;
        }

    }

    @Override
    public Cart getSingleCart(String redisKey) {
        Cart cart = new Cart();
        List<CartItem> cartItems = getCartItems(redisKey);
        cart.setItems(cartItems);
        cart.setReduce(new BigDecimal(0));
        return cart;
    }

    @Override
    public Cart getMultiCartAndMerge(List<String> redisKey) {
        Cart cart = new Cart();
        List<CartItem> cartItems = new ArrayList<>();
        for (String key : redisKey) {
            cartItems.addAll(getCartItems(key));
        }

        Map<String, CartItem> redisSaveCartMap = new HashMap<>(cartItems.size());
        cartItems.forEach(cartItem -> {
            Long skuId = cartItem.getSkuId();
            if (redisSaveCartMap.containsKey(skuId.toString())) {
                CartItem c = redisSaveCartMap.get(skuId.toString());
                c.setCount(c.getCount() + cartItem.getCount());
                redisSaveCartMap.put(skuId.toString(), c);
            } else {
                redisSaveCartMap.put(skuId.toString(), cartItem);
            }
        });


        // 合并并写入redis再展示
        UserKeyInfo userKeyInfo = CookieInterceptor.userKeyInfoThreadLocal.get();
        if (userKeyInfo.getUserId() != null) {
            // 提交到用户购物车
            String uKey = CartConstant.USER_KEY_SAVE_PREFIX + userKeyInfo.getUserId();
            redisTemplate.opsForHash().putAll(uKey, redisSaveCartMap);
        }
        if (userKeyInfo.getUserKey() != null) {
            // 移除临时购物车
            String tempKey = CartConstant.USER_KEY_SAVE_PREFIX + userKeyInfo.getUserKey();
            redisTemplate.delete(tempKey);
        }
        cart.setItems(new ArrayList<>(redisSaveCartMap.values()));
        cart.setReduce(new BigDecimal(0));
        return cart;
    }

    @Override
    public void subProductNum(Long skuId, Integer subNum) {
        String key = skuId.toString();
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        CartItem cartItem = getCurrCartItemBySkuId(skuId, cartOps);
        // 商品减少后值大于0，修改值后保存
        if (cartItem != null) {
            int curCount = cartItem.getCount() - subNum;
            if (curCount > 0) {
                cartItem.setCount(curCount);
                cartOps.put(key, cartItem);
            } else if (curCount == 0) {
                // 等于0，删除商品
                cartOps.delete(key);
            }
        }
    }

    @Override
    public void addProductNum(Long skuId, Integer addNum) {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        CartItem cartItem = getCurrCartItemBySkuId(skuId, cartOps);
        // 增加商品
        if (cartItem != null) {
            cartItem.setCount(cartItem.getCount() + addNum);
            cartOps.put(skuId.toString(), cartItem);
        }
    }

    @Override
    public void changeSkuNum(Long skuId, Integer modifyNum) {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        CartItem cartItem = getCurrCartItemBySkuId(skuId, cartOps);
        if (cartItem != null) {
            cartItem.setCount(modifyNum);
            cartOps.put(skuId.toString(), cartItem);
        }

    }

    private CartItem getCurrCartItemBySkuId(Long skuId, BoundHashOperations<String, Object, Object> cartOps) {
        JSONObject item = (JSONObject) cartOps.get(skuId.toString());
        if (item != null) {
            return item.toJavaObject(CartItem.class);
        }
        return null;
    }

    @Override
    public void deleteProduct(Long skuId) {
        getCartOps().delete(skuId.toString());
    }


    /**
     * 获取购物车中商品
     * @param redisKey
     * @return
     */
    public List<CartItem> getCartItems(String redisKey) {
        HashOperations<String, Object, Object> opsForHash = redisTemplate.opsForHash();

        List<CartItem> cartItems = new ArrayList<>();
        Set<Object> keys = opsForHash.keys(redisKey);
        if (keys.size() > 0) {
            List<Object> cartItem = opsForHash.multiGet(redisKey, keys);
            cartItem.forEach(c -> {
                JSONObject item = (JSONObject) c;
                cartItems.add(JSON.toJavaObject(item, CartItem.class));
            });
        }
        return cartItems;
    }

    @Override
    public void changeItemCheck(Long skuId) {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        JSONObject item = (JSONObject) cartOps.get(skuId.toString());
        if (item != null) {
            CartItem cartItem = item.toJavaObject(CartItem.class);
            // 设置反选
            cartItem.setCheck(!cartItem.isCheck());
            cartOps.put(skuId, cartItem);
        }
    }

    private BoundHashOperations<String, Object, Object> getCartOps() {
        ThreadLocal<UserKeyInfo> userKeyInfoThreadLocal = CookieInterceptor.userKeyInfoThreadLocal;
        UserKeyInfo userKeyInfo = userKeyInfoThreadLocal.get();

        // 已登录
        Long userId = userKeyInfo.getUserId();
        String userKey = userKeyInfo.getUserKey();
        String redisKey = "";
        if (userId != null) {
            redisKey = CartConstant.USER_KEY_SAVE_PREFIX + userId;
        } else if (userKey != null) {
            redisKey = CartConstant.USER_KEY_SAVE_PREFIX + userKey;
        }
        return redisTemplate.boundHashOps(redisKey);
    }
}
